/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Controlador.Service.Simulacion;

import Controlador.Bean.Simulacion.MaquinaS;
import Controlador.Bean.Simulacion.Rendimiento;
import Controlador.Bean.Simulacion.Trabajador;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

/**
 * la estructura del siguiente beanFunction es la siguiente: 1.Funcion principal
 * 2.Funciones para crear soluciones 3.Funciones para hacer el paso generacional
 * 4.Funcion desempenho y utilidad
 *
 * @author Victor
 */
public class AlgMemetico {
    //constantes para la presentacion del viernes

    private static final double PRECIOVENTA = 2;
    private static final int DEMANDA = 1000;
    private static final int BONUSPORCEROMERMA = 100;
    private static final int ITCONDMESETA = 400;
    //variables estaticas del algoritmo(aun no se les da uso)
    private static int cambioMaquina = 0;//indica si se cargo una nueva lista de maquinas
    private static int cambioTrabajador = 0;//inidica si se cargo una nueva lista de trabajadores       
    private int itConvergencia = 0;
    //El uso de hashmap se justifica en la rapidez de acceso a los datos y en ek orden aleatorio en el
    //cual se guardan los datos.
    private HashMap<String, Trabajador> trabajadores;//lista de los datos de los trabajadores
    private ArrayList<String> codTrabajadoresOriginal;//Lista de codigos base de los trabajadores disponibles para ser asignados
    private ArrayList<String> CodTrabajadores;//lista de codigo de trabajadores
    private List<String> turnos;
    private HashMap<String, MaquinaS> maquinas;//lista de los datos de las maquinas
    private ArrayList<String> codMaquinas;//lista de codigo de maquinas
    private HashMap<String, HashMap<String, Double>> desempenhoxTrabajadorxTipoMaquina = null;
    String tipoMaquinaActual;
    String turnoActual;
    private TreeSet<Map.Entry<String, Double>> auxDesempenhoxTrabajadorxTipoMaquina;
    private ArrayList<Map.Entry<String, Double>> auxDesempenhoxTrabajadorxTipoMaquinaList;
    private ArrayList<String> tipoMaquinas;//lista de los tipos de maquina        
    //private HashMap<String,TipoMaquinaBeanData> tipoMaquinas;//lista de los tipos de maquina        
    private static SortedMap<String, Integer> maquinasxTipo;//contador de maquinas por cada tipo
    private SortedMap<String, Integer> auxMaquinasxTipo;//contador de maquinas por cada tipo, modificable
    private static SortedMap<String, Double> mermaxTipoMaquina;//merma por cada tipo de maquina    
    private int trabajadoresSize;//numero de trabajadores disponibles para asignar
    private HashMap<String,Integer> planProdXTurno;//limitante de la produccion por turno
    private HashMap<String,Double> prodAsignadaxTurno;//limitante de la produccion por turno
    /**
     * Funcion principal que ejcuta el algoritmo, devuelve un hashmap que
     * contiene para cada maquina el trabajador asignado. Recibe como parametros
     * en tamaño de cada poblacion(numero de soluciones en la poblacion),el
     * porcentaje de soluciones a conservar cuando hay una convergencia y la
     * catidad de pasos generacionales a hacer
     *
     */
    public HashMap<String, String> ALG01(int n, double conservar, int iteracionesxturno, double alfa) throws Exception {

        ArrayList<HashMap<String, String>> soluciones = new ArrayList<>();//guarda las soluciones generadas en el paso generacional anterior
        ArrayList<HashMap<String, String>> nuevassoluciones = new ArrayList<>();//guarda las nuevas solucines generadas en el paso generacional actual
        ArrayList<Object> auxiliarLst;//guarda 2 objetos:maximo rendimiento de todas la poblaciones hasta ahora y el rendimiento de todas las soluciones en la poblacion que se evaluo
        HashMap<String, String> auxiliar = new HashMap<>();//guarda temporalmente las soluciones de la poblacion inicial        
        TreeSet<Map.Entry<Integer, Double>> utilidadxSolucion;//
        boolean cParada = true;//en este caso solo es el numero de iteraciones        
        int iteraciones = 0;
        //calculamos la funcion de desempenho para todos los trabajadores por cada tipo de maquina
        this.CodTrabajadores = new ArrayList<>(this.getCodTrabajadoresOriginal());
        if (this.cambioTrabajador != 0) {
            calcularFuncionDesempenhoTrabajadores();
        }
        //primera generacion                
        for (int j = 0; j < n; j++) {
            auxiliar = this.GenerarSolucion(alfa);
            soluciones.add(auxiliar);
        }
        auxiliarLst = this.promedioUtilidadPoblacion(soluciones);//sacamos el promedio de utilidad de la poblacion inicial
        double maxRendimientodPoblacional = ((Double) auxiliarLst.get(0)).doubleValue();//aca se va guarda el maximo rendimiento 
        //poblacional hasta ahora que es el de la poblacion inicial)
        utilidadxSolucion = (TreeSet<Map.Entry<Integer, Double>>) auxiliarLst.get(1);//Aca se guarda la utilidad de cada solucion en la poblacion inicial
        double rendimientoPoblacional = 0;//guarda el rendmiento de una poblacion
        while (cParada) {
            nuevassoluciones = pasoGeneracional(soluciones);//se generan nuevas soluciones "casando" a las soluciones de la poblaciona anterior 
            auxiliarLst = this.promedioUtilidadPoblacion(nuevassoluciones);//sacamos el promedio de utilidad de la nueva poblacion
            rendimientoPoblacional = ((Double) auxiliarLst.get(0)).doubleValue();//aca se va guarda el rendimiento de la nueva poblacion
            utilidadxSolucion = (TreeSet<Map.Entry<Integer, Double>>) auxiliarLst.get(1); // aca se guarda el rendimiento de cada solucion en la nueva poblacion
            if (rendimientoPoblacional < maxRendimientodPoblacional) { // se verifica si se llego a una convergencia(solucion nueva peor que la mejor de las anteriores)
                nuevassoluciones = this.reinicioPoblacion(nuevassoluciones, conservar, utilidadxSolucion, alfa);//se reinicia la poblacion(se reiran reemplazan peores soluciones por nuevas)
            } else {
                maxRendimientodPoblacional = rendimientoPoblacional;//como la nueva solucion es mejor, su rendimiento es ahora el maximo                            
            }
            soluciones = nuevassoluciones;//la nueva solucion se convierte en antigua
            iteraciones++;//contador de iteraciones
            cParada = (iteraciones > iteracionesxturno) ? false : true;//se verifica la condicion de parada

        }
        return soluciones.get(utilidadxSolucion.last().getKey());// se retorna la mejor solucion
    }

    /**
     * ****En esta seccion estan las funciones dedicadas a generar una nueva solucion******
     */
    /**
     * Esta funcion se encarga de contar las maquinas que hay por cada tipo
     */
    private void contarMaquinasxTipo() {
        TreeMap<String, Integer> nuevoMaquinasxTipo = new TreeMap<String, Integer>(); //guardas el numero de maquinas x tipo       
        TreeMap<String, Double> nuevoMermaxTipoMaquina = new TreeMap<String, Double>(); //guarda el costo de la merma maxima de las maquinas x cada tipo
        Iterator it = this.maquinas.entrySet().iterator();
        String tipoMaquina;//guarda el tipo de maquina en cada iteracion
        Double costoMerma;//guarda el costo de merma en cada iteracion
        while (it.hasNext()) {//hasta que se acabe la lista de maquinas
            MaquinaS maquina = (MaquinaS) ((Map.Entry) it.next()).getValue();//guarda todos lo datos de la maquina
            tipoMaquina = maquina.getTipo();//se guarda solo el tipo de maquina
            costoMerma = Double.valueOf(maquina.getCostoMerma());//se guarda solo el costo de la merma de la maquina
            if(planProdXTurno.get(tipoMaquina)!=null && planProdXTurno.get(tipoMaquina)>0){
                nuevoMaquinasxTipo.put(tipoMaquina, (nuevoMaquinasxTipo.get(tipoMaquina) == null ? 0 : nuevoMaquinasxTipo.get(tipoMaquina)) + 1);// se aumenta el contador de maquinas para el tipo maquina encontrado            
            if (nuevoMermaxTipoMaquina.get(tipoMaquina) == null || costoMerma > nuevoMermaxTipoMaquina.get(tipoMaquina))//se guarda siempre la merma maxima x cada tipo
            {
                nuevoMermaxTipoMaquina.put(tipoMaquina, costoMerma);
            }
            }
        }
        mermaxTipoMaquina = nuevoMermaxTipoMaquina;//se asigna el arreglo de mermas a la variable global de la funcion
        maquinasxTipo = nuevoMaquinasxTipo;//se asigna el arreglo de numero de maquinas a la variable global de la funcion
        this.cambioMaquina = 0;//indica que ya se proceso la nueva lista de maquinas
    }

    private void calcularFuncionDesempenhoTrabajadores() {
        int sizeM = tipoMaquinas.size();
        int sizeT = CodTrabajadores.size();
        desempenhoxTrabajadorxTipoMaquina = new HashMap<>();
        //por cada trabajador se calcula su desempenho para cada tipo de maquina
        for (int j = 0; j < sizeT; j++) {
            String codTrabajador = CodTrabajadores.get(j);
            HashMap<String, Rendimiento> rendimiento = trabajadores.get(codTrabajador).getRendimiento();
            HashMap<String, Integer> indice = trabajadores.get(codTrabajador).getIndice();
            HashMap<String, Double> desempenho = new HashMap<>();
            for (int i = 0; i < sizeM; i++) {
                String tipoMaquina = tipoMaquinas.get(i);
                if(planProdXTurno.get(tipoMaquina)==null || planProdXTurno.get(tipoMaquina)<=0) continue;
                for (int k = 0; k < turnos.size(); k++) {
                    desempenho.put("" + turnos.get(k) + "." + tipoMaquina, this.funcionDesempenho(rendimiento.get(tipoMaquina), tipoMaquina, turnos.get(k)));
                }
            }
            desempenhoxTrabajadorxTipoMaquina.put(codTrabajador, desempenho);
        }
        this.cambioTrabajador = 0;
    }

    /**
     * sta funcion entre una solucion posible
     */
    public HashMap<String, String> GenerarSolucion(double alfa) throws Exception {

        HashMap<String, ArrayList<String>> trabajadorxTipo = new HashMap<>();//aca se guardara los codigos de la trabajadores por cada tipo de maquina
        if (auxMaquinasxTipo == null) {
            auxMaquinasxTipo = new TreeMap<>();// se hace una copia de las maquinas x tipo
        }
        auxMaquinasxTipo.clear();                                   // para no alterar la cuenta original y reusarla en        
        auxMaquinasxTipo.putAll(maquinasxTipo);                     // la generacion de otras soluciones

        //auxdesempenhoxTrabajadorxTipoMaquina= new HashMap<>(this.desempenhoxTrabajadorxTipoMaquina);

        trabajadoresSize = CodTrabajadores.size();//renicia el numero de trabajadores
        int maquinaSize = codMaquinas.size();//reinicia el numero de maquinas        
        HashMap<String, String> solucion = new HashMap<>(); //aca se guardara la solucion generada(asignacion maquina-trabajador)       

        tipoMaquinaActual = null;
        turnoActual = null;
        //por cada tipo de maquina se van asignando trabajadores
        for (int l = 0; l < turnos.size(); l++) {
            prodAsignadaxTurno= new HashMap<>();
            Iterator it = auxMaquinasxTipo.entrySet().iterator();
            Map.Entry<String, Integer> e = (Map.Entry<String, Integer>) it.next();
            int k = 0;
            while (true) {
                k++;
                if (k > e.getValue() || (prodAsignadaxTurno.get(e.getKey())!=null && prodAsignadaxTurno.get(e.getKey())>planProdXTurno.get(e.getKey()))) {//se le asignan trabajadores a todas las maquinas de un tipo antes de pasar al siguiente
                    k = 1;
                    if (!it.hasNext()) {
                        break;
                    }
                    e = (Map.Entry<String, Integer>) it.next();
                }
                String codTrabajador = this.asignarTipoMaquinaATrabajador(alfa, e.getKey(), turnos.get(l));//se selecciona el trabajador
                if (codTrabajador != null) {
                    prodAsignadaxTurno.put(e.getKey(), (prodAsignadaxTurno.get(e.getKey())==null?0:prodAsignadaxTurno.get(e.getKey()))+trabajadores.get(codTrabajador).getRendimiento().get(e.getKey()).getPorTurno().get(String.valueOf(turnos.get(l))).get("produccion"));
                    if (trabajadorxTipo.get(turnos.get(l) + "." + e.getKey()) == null) {//se guarda el tipo asignado al trabajador en un arraylist
                        ArrayList<String> trabajadores = new ArrayList<>();
                        trabajadores.add(codTrabajador);
                        trabajadorxTipo.put(turnos.get(l) + "." + e.getKey(), trabajadores);
                    } else {
                        trabajadorxTipo.get(turnos.get(l) + "." + e.getKey()).add(codTrabajador);
                    }
                    getCodTrabajadores().remove(codTrabajador);
                } else {
                    break;
                }
            };
        }
        try{
        for (int l = 0; l < turnos.size(); l++) {
            for (int i = 0; i < maquinaSize; i++) {//de acuerdo al tipo asignado al trabajador se le asgina una maquina
                String codMaquina = codMaquinas.get(i);
                String tipoMaquina = maquinas.get(codMaquina).getTipo();
                if(trabajadorxTipo.get(turnos.get(l) + "." + tipoMaquina)!=null && !trabajadorxTipo.get(turnos.get(l) + "." + tipoMaquina).isEmpty()){
                    solucion.put(turnos.get(l) + "." + codMaquina, trabajadorxTipo.get(turnos.get(l) + "." + tipoMaquina).get(0));
                    trabajadorxTipo.get(turnos.get(l) + "." + tipoMaquina).remove(0);
                }
            }
        }
        }
        catch(Exception e){     
            e.printStackTrace();
        }
        this.setCodTrabajadores(new ArrayList<String>(this.getCodTrabajadoresOriginal()));//se reinicia la lista de codigos de trabajdor disponibles para la siguieten genracion de solucion
        return solucion;
    }

    /**
     * Esta funcion le asigna un trabajador a un tipo de maquina
     */
    private String asignarTipoMaquinaATrabajador(double alfa, String tipoMaquina, String turno) {
        //Hallamos el limite inferior del RCL
        try{
        Double mayor = null, menor = null;
        Iterator<String> it = this.getCodTrabajadores().iterator();//recorre los codigos de trabajadores
        if (tipoMaquinaActual == null || !tipoMaquinaActual.equals(tipoMaquina) || turnoActual == null || !turnoActual.equals(turno)) {//se ejecuta una vez por tipo de maquina
            auxDesempenhoxTrabajadorxTipoMaquina = new TreeSet<>(new Comparador());
            while (it.hasNext()) {
                String codTrabajador = it.next();
                HashMap<String, Double> e = desempenhoxTrabajadorxTipoMaquina.get(codTrabajador);
                Double desempenho = e.get(turno + "." + tipoMaquina);
                if (mayor == null || desempenho.compareTo(mayor) > 0) {
                    mayor = desempenho;
                }
                if (menor == null || desempenho.compareTo(menor) < 0) {
                    menor = desempenho;
                }
                //Se ordenan los trabajadores de acuerdo a su desempenho con el tipo de maquina
                auxDesempenhoxTrabajadorxTipoMaquina.add(new MapKey<String, Double>(codTrabajador, desempenho));
            }
            tipoMaquinaActual = tipoMaquina;//se actualiza el tipo de maquina con el cual se esta trabjando
            turnoActual = turno;
            auxDesempenhoxTrabajadorxTipoMaquinaList= new ArrayList<>(auxDesempenhoxTrabajadorxTipoMaquina);
        } else {
            //como ya se tienen los desempenho ordenados solo se saca el amyor y el menor            
            menor = auxDesempenhoxTrabajadorxTipoMaquinaList.get(0).getValue();
            mayor = auxDesempenhoxTrabajadorxTipoMaquinaList.get(auxDesempenhoxTrabajadorxTipoMaquinaList.size()-1).getValue();
        }
        //limite inferior del RCL
        Double limiteInferiorRCL = mayor - (mayor - menor) * alfa;
        Integer contador = 0;
        //Iterator<Map.Entry<String, Double>> itDesempenho = auxDesempenhoxTrabajadorxTipoMaquinaList.descendingIterator();
        //contamos los trabajadores que estan en el RCL   
        for(int i=(auxDesempenhoxTrabajadorxTipoMaquinaList.size()-1);i>=0;i--){
            Double desempenho = auxDesempenhoxTrabajadorxTipoMaquinaList.get(i).getValue();
            if (desempenho.compareTo(limiteInferiorRCL) >= 0) {
                contador++;
            } else {
                break;
            }
        }
                Random rnd = new Random();//se selecciona un trabajador al azar de RCL
        Integer rndInt = null;
        try {
            rndInt = rnd.nextInt(contador);
        } catch (Exception e) {
        }
        //itDesempenho = auxDesempenhoxTrabajadorxTipoMaquina.descendingIterator();
        String codTrabajador = null;
        contador = 0;
        Map.Entry<String, Double> auxE = null;
        //se encuentra y extrae el trabajador seleccionado
        int remover=0;
        for(int i=(auxDesempenhoxTrabajadorxTipoMaquinaList.size()-1);i>=0;i--){
            auxE = auxDesempenhoxTrabajadorxTipoMaquinaList.get(i);
            codTrabajador = auxE.getKey();
            if (contador < rndInt) {
                contador++;
            } else {
                remover=i;
                break;
            }
        }
         auxDesempenhoxTrabajadorxTipoMaquinaList.remove(remover);
        return codTrabajador;
        }
        catch(Exception e){
            //e.printStackTrace();
            return null;
        }
    }

    /**
     * Como su nombre lo dice, esta funcion genera una solucion
     */
//    public HashMap<String,String>GenerarSolucion(double alfa) throws Exception{
//                
//        HashMap<String,ArrayList<String>> trabajadorxTipo= new HashMap<>();//aca se guardara los codigos de la trabajadores por cada tipo de maquina
//        if(auxMaquinasxTipo==null) auxMaquinasxTipo=new TreeMap<>();// se hace una copia de las maquinas x tipo
//        auxMaquinasxTipo.clear();                                   // para no alterar la cuenta original y reusarla en        
//        auxMaquinasxTipo.putAll(maquinasxTipo);                     // la generacion de otras soluciones
//        
//        trabajadoresSize=CodTrabajadores.size();//renicia el numero de trabajadores
//        int maquinaSize=codMaquinas.size();//reinicia el numero de maquinas        
//        HashMap<String,String> solucion= new HashMap<>(); //aca se guardara la solucion generada(asignacion maquina-trabajador)       
//        
//        for(int i=0;i<maquinaSize;i++){
//            
//            String codTrabajador= this.SeleccionarTrabajador(); //se selecciona un trabajador al azar          
//            if(codTrabajador!=null) {
//                String tipoMaquinaAsignado=this.asignarTipoMaquina(codTrabajador,alfa);//se obtiene el tipo asignado al trabajador
//                if(trabajadorxTipo.get(tipoMaquinaAsignado)==null) {//se guarda el tipo asignado en un arraylist
//                    ArrayList<String> trabajadores= new ArrayList<>();
//                    trabajadores.add(codTrabajador);                    
//                    trabajadorxTipo.put(tipoMaquinaAsignado, trabajadores);
//                }
//                else{
//                    trabajadorxTipo.get(tipoMaquinaAsignado).add(codTrabajador);
//                }                
//            }          
//            else break;
//        };        
//        for(int i=0;i<maquinaSize;i++){//de acuerdo al tipo asignado al trabajador se le asgina una maquina
//            String codMaquina=codMaquinas.get(i);
//            String tipoMaquina=maquinas.get(codMaquina).getTipo();
//            solucion.put(codMaquina, trabajadorxTipo.get(tipoMaquina).get(0));
//            trabajadorxTipo.get(tipoMaquina).remove(0);            
//        }
//        this.setCodTrabajadores(new ArrayList<String>(trabajadores.keySet()));//se reinicia la lista de codigos de trabajdor disponibles para la siguieten genracion de solucion
//        return solucion;
//    }
    /**
     * ****En esta seccion estan las funciones dedicadas al paso genracional******
     */
    /**
     * Esta funcion se dedica a "casar" a las soluciones y devielve sus "hijos"
     */
    private ArrayList<HashMap<String, String>> reproducir(HashMap<String, String> sol1, HashMap<String, String> sol2) {
        //cruze multipunto
        Iterator it = sol1.entrySet().iterator();//recorre la solucion 1
        HashMap<String, String> nsol1 = new HashMap<>();//se guarda el hijo 1
        HashMap<String, String> nsol2 = new HashMap<>();//se guarda el hijo 2
        HashMap<String, Integer> trabajadorSol = new HashMap<>();
        ArrayList<HashMap<String, String>> resultado = new ArrayList<>();
        while (it.hasNext()) {
            Map.Entry e = (Map.Entry) it.next();
            String codMaquina = (String) e.getKey();//se obtiene un codigo de maquina
            if (trabajadorSol.get(sol1.get(codMaquina)) == null && trabajadorSol.get(sol2.get(codMaquina)) == null) {
                /*Se compara para esa maquina que solucion asigno al mejor trabajador de acuerdo a la funcion de desempenho*/
                if (funcionDesempenhoReal(codMaquina, sol1.get(codMaquina)).compareTo(funcionDesempenhoReal(codMaquina, sol2.get(codMaquina))) >= 0) {
                    nsol1.put(codMaquina, sol1.get(codMaquina));
                    nsol2.put(codMaquina, sol2.get(codMaquina));
                    trabajadorSol.put(sol1.get(codMaquina), 1);
                    trabajadorSol.put(sol2.get(codMaquina), 2);
                } else {
                    nsol1.put(codMaquina, sol1.get(codMaquina));
                    nsol2.put(codMaquina, sol2.get(codMaquina));
                }
            } /*en caso el trabajador ya se le ha asignado en una comparacion anterior una maquina, se asegura que en una solucion no haya un trabajador a 2 maquinas*/ else {
                if (trabajadorSol.get(sol1.get(codMaquina)) == 1 && trabajadorSol.get(sol2.get(codMaquina)) == 2) {
                    nsol2.put(codMaquina, sol1.get(codMaquina));
                    nsol1.put(codMaquina, sol2.get(codMaquina));
                } else {
                    nsol1.put(codMaquina, sol1.get(codMaquina));
                    nsol2.put(codMaquina, sol2.get(codMaquina));
                }
            }
        }
        resultado.add(sol1);
        resultado.add(sol2);
        return resultado;
    }

    /**
     * Esta funcion segun el % a conservar indicado, reemplaza las peores
     * soluciones por nuevas
     */
    private ArrayList<HashMap<String, String>> reinicioPoblacion(ArrayList<HashMap<String, String>> soluciones, double conservar, TreeSet<Map.Entry<Integer, Double>> utilidadxSolucion, double alfa) throws Exception {
        int retirar = (int) (soluciones.size() - soluciones.size() * conservar);//cantidad de soluciones a retirar
        ArrayList<HashMap<String, String>> retirados = new ArrayList<>();//guarda las soluciones a retirar
        ArrayList<HashMap<String, String>> nuevos = new ArrayList<>();//guarda las soluciones nuevas
        TreeSet<Map.Entry<Integer, Double>> temporal = utilidadxSolucion;//guarda de manera ordenada las soluciones, de acuerda a su rendimiento
        for (int i = 0; i < retirar; i++) {
            nuevos.add(this.GenerarSolucion(alfa));//se crean nuevas soluciones
        }
        Double minUtilidad = null;
        Iterator it = temporal.iterator();
        while (retirar > 0) {
            Map.Entry<Integer, Double> aRetirar = (Map.Entry<Integer, Double>) it.next();//se seleccionan las soluciones a retirar
            retirados.add(soluciones.get(aRetirar.getKey()));
            //temporal.remove(temporal.first());
            retirar--;
        }
        soluciones.removeAll(retirados);//se retiran las soluciones
        soluciones.addAll(nuevos);//se agregan las soluciones

        return soluciones;
    }

    /**
     * esat funcion ejecuta todos los pasos para crear una nueva solucion a
     * partir de una anterior
     */
    private ArrayList<HashMap<String, String>> pasoGeneracional(ArrayList<HashMap<String, String>> soluciones) {
        Random rnd = new Random();
        HashMap<String, String> auxiliar1, auxiliar2;
        ArrayList<HashMap<String, String>> resultado = new ArrayList<>();//guarda la nueva poblacion
        ArrayList<HashMap<String, String>> auxiliar = new ArrayList<>();//guarda ls nuevas soluciones
        int numAleatorio;
        while (soluciones.size() > 0) {
            numAleatorio = rnd.nextInt(soluciones.size() > 1 ? soluciones.size() - 1 : 1);
            auxiliar1 = soluciones.get(numAleatorio);//se selecciona una solucion al azar para que se reproduzca
            soluciones.remove(auxiliar1);//y se la retira de de la poblacion antigua
            if (soluciones.size() == 0) {//en caso el numero de la poblacion es impar la ultima solucion no se reproduce, para directamente a la nueva poblacion
                resultado.add(auxiliar1);
            } else {
                numAleatorio = rnd.nextInt(soluciones.size() > 1 ? soluciones.size() - 1 : 1);
                auxiliar2 = soluciones.get(numAleatorio);//se selecciona una solucion al azar para que se reproduzca
                soluciones.remove(auxiliar2);//y se la retira de de la poblacion antigua        
                auxiliar = reproducir(auxiliar1, auxiliar1);

                resultado.addAll(auxiliar);//se agregan las nuevas soluciones a la nueva poblacion                              
            }
        }
        return resultado;
    }

    /**
     * ****En esta seccion estan las funciones de desempenho y de utilidad******
     */
    /**
     * esta funcion encuentra el desempenho real de los trabajdores con el tipo
     * de maquina
     */
    private Double funcionDesempenhoReal(String codMaquina, String codTrabajador) {
        String turno = codMaquina.split("\\.")[0];
        MaquinaS maquina = maquinas.get(codMaquina.split("\\.")[1]);
        Trabajador trabajador = trabajadores.get(codTrabajador);
        Double produccion = trabajador.getRendimiento().get(maquina.getTipo()).getPorTurno().get("" + turno).get("produccion");
        Double perdida = trabajador.getRendimiento().get(maquina.getTipo()).getPorTurno().get("" + turno).get("perdida");
        if (produccion == 0 && perdida > 0) {
            return Double.valueOf("-1");
        } else if (produccion == 0 && perdida == 0) {
            return Double.valueOf("0");
        } else if (perdida == 0) {
            return (BONUSPORCEROMERMA * (produccion * PRECIOVENTA));
        };
        Double resultado = (produccion / perdida) * (produccion * PRECIOVENTA - maquina.getCostoMerma());

        return resultado;
    }

    /**
     * esta funcion encuentra el desempenho de los trabajdores con el tipo de
     * maquina, usando el costo de merma maximo x tipo de maquina private Double
     * funcionDesempenho(String codTrabajador,String tipo,String turno){
     * Trabajador trabajador= trabajadores.get(codTrabajador); Double
     * produccion=
     * trabajador.getRendimiento().get(tipo).getPorTurno().get(""+turno).get("produccion");
     * Double
     * perdida=trabajador.getRendimiento().get(tipo).getPorTurno().get(""+turno).get("perdida");
     * if(produccion==0 && perdida>0) return Double.valueOf("-1"); else
     * if(produccion==0 && perdida==0) return Double.valueOf("0"); else
     * if(perdida==0) return(BONUSPORCEROMERMA * (produccion *PRECIOVENTA));;
     * Double
     * resultado=(produccion/perdida)*(produccion*PRECIOVENTA-this.mermaxTipoMaquina.get(tipo));
     * return resultado;
    }
     */
    /**
     * Esta funcion calcula el desempenho dado un rendimiento de trabjador
     */
    private Double funcionDesempenho(Rendimiento rendimiento, String tipo, String turno) {
        Double produccion = rendimiento.getPorTurno().get(String.valueOf(turno)).get("produccion");
        Double perdida = rendimiento.getPorTurno().get(String.valueOf(turno)).get("perdida");
        if (produccion == 0 && perdida > 0) {
            return Double.valueOf("-1");
        } else if (produccion == 0 && perdida == 0) {
            return Double.valueOf("0");
        } else if (perdida == 0) {
            return (BONUSPORCEROMERMA * (produccion * PRECIOVENTA));
        };
        Double resultado = (produccion / perdida) * (produccion * PRECIOVENTA - this.mermaxTipoMaquina.get(tipo));
        return resultado;
    }

    /**
     * esta funcion encuentra la utilidad total de una solucion(funcion de fitness)
     */
    public double funcionUtilidad(HashMap<String, String> Solucion) {
        Iterator it = Solucion.entrySet().iterator();
        double Costo = 0, Venta = 0;
        String codMaquina;
        while (it.hasNext()) {

            codMaquina = (String) ((Map.Entry) it.next()).getKey();
            String turno = codMaquina.split("\\.")[0];
            codMaquina = codMaquina.split("\\.")[1];
            MaquinaS maquina = maquinas.get(codMaquina);
            Trabajador trabajador = trabajadores.get(Solucion.get(turno + "." + codMaquina));
            Rendimiento rendTrabjador = trabajador.getRendimiento().get(maquina.getTipo());
            if ("TMA000004".equals(maquina.getTipo())) {
                //Venta +=  (PRECIOVENTA * (maquina.getProduccion()* rendTrabjador.getProduccion()-maquina.getProduccion()* rendTrabjador.getProduccion() *rendTrabjador.getPerdida()));          
                Venta += (PRECIOVENTA * (maquina.getProduccion() * rendTrabjador.getPorTurno().get("" + turno).get("produccion")));
            } else {
                Venta += (maquina.getCostoMerma() * (maquina.getProduccion() * rendTrabjador.getPorTurno().get("" + turno).get("produccion")));
            }
            //Costo += maquina.getProduccion()* rendTrabjador.getProduccion() *rendTrabjador.getPerdida()* maquina.getCostoMerma();
            Costo += rendTrabjador.getPorTurno().get("" + turno).get("perdida") * maquina.getCostoMerma();
        }
        return Venta - Costo;
    }

    /**
     * Esta funcion obtiene el promedio de utilida para una poblacion
     */
    public ArrayList<Object> promedioUtilidadPoblacion(ArrayList<HashMap<String, String>> soluciones) {
        double suma = 0;
        double utilidadSolucion;
        ArrayList<Object> resultado = new ArrayList<>();
        TreeSet<Map.Entry<Integer, Double>> auxiliarUtilidadxPoblacion = new TreeSet<Map.Entry<Integer, Double>>(new Comparador());
        HashMap<Integer, Double> temporal = new HashMap<>();
        for (int i = 0; i < soluciones.size(); i++) {//se suman las utilidades de las soluciones en una poblacion
            utilidadSolucion = this.funcionUtilidad(soluciones.get(i));
            temporal.put(i, utilidadSolucion);
            suma += utilidadSolucion;
        }
        Iterator it = temporal.entrySet().iterator();
        while (it.hasNext()) {
            auxiliarUtilidadxPoblacion.add((Map.Entry<Integer, Double>) it.next());
        }
        resultado.add(Double.valueOf(suma / soluciones.size()));//se guarda el promedio de utilidad
        resultado.add(auxiliarUtilidadxPoblacion);//se guarda la utilidad de cada solucion en la poblacion
        return resultado;

    }

    //SETTERS AND GETTERS
    /**
     * @return the trabajadores
     */
    public HashMap<String, Trabajador> getTrabajadores() {
        return trabajadores;
    }

    /**
     * @param trabajadores the trabajadores to set
     */
    public void setTrabajadores(HashMap<String, Trabajador> trabajadores) {
        this.cambioTrabajador = 1;//se cambio la lista de trabajadores
        //this.setCodTrabajadores(new ArrayList<>(trabajadores.keySet()));
        this.trabajadores = trabajadores;
    }

    /**
     * @return the codMaquinas
     */
    public ArrayList<String> getCodMaquinas() {
        return codMaquinas;
    }

    /**
     * @param codMaquinas the codMaquinas to set
     */
    private void setCodMaquinas(ArrayList<String> codMaquinas) {
        this.codMaquinas = codMaquinas;
    }

    /**
     * @return the maquinasDisponibles
     */
    public HashMap<String, MaquinaS> getMaquinasDisponibles() {
        return maquinas;
    }

    /**
     * @param maquinasDisponibles the maquinasDisponibles to set
     */
    public void setMaquinasDisponibles(HashMap<String, MaquinaS> maquinasDisponibles) {
        this.cambioMaquina = 1;//se cambio la lista de maquinas
        this.setCodMaquinas(new ArrayList<>(maquinasDisponibles.keySet()));
        this.maquinas = maquinasDisponibles;
        this.contarMaquinasxTipo();//si se cambio la lista de maquina se cuentan de nuevo
    }

    /**
     * @return the CodTrabajadores
     */
    public ArrayList<String> getCodTrabajadores() {
        return CodTrabajadores;
    }

    /**
     * @param CodTrabajadores the CodTrabajadores to set
     */
    private void setCodTrabajadores(ArrayList<String> CodTrabajadores) {
        this.CodTrabajadores = CodTrabajadores;
    }

    /**
     * @return the tipoMaquinas
     */
    public ArrayList<String> getTipoMaquinas() {
        return tipoMaquinas;
    }

    /**
     * @param tipoMaquinas the tipoMaquinas to set
     */
    public void setTipoMaquinas(ArrayList<String> tipoMaquinas) {
        this.tipoMaquinas = tipoMaquinas;
    }

    /**
     * @return the codTrabajadoresOriginal
     */
    public ArrayList<String> getCodTrabajadoresOriginal() {
        return codTrabajadoresOriginal;
    }

    /**
     * @param codTrabajadoresOriginal the codTrabajadoresOriginal to set
     */
    public void setCodTrabajadoresOriginal(ArrayList<String> codTrabajadoresOriginal) {
        CodTrabajadores = new ArrayList<>(codTrabajadoresOriginal);
        this.codTrabajadoresOriginal = codTrabajadoresOriginal;
    }

    /**
     * @return the turnos
     */
    public List<String> getTurnos() {
        return turnos;
    }

    /**
     * @param turnos the turnos to set
     */
    public void setTurnos(List<String> turnos) {
        this.turnos = turnos;
    }

    /**
     * @return the planProdXTurno
     */
    public HashMap<String,Integer> getPlanProdXTurno() {
        return planProdXTurno;
    }

    /**
     * @param planProdXTurno the planProdXTurno to set
     */
    public void setPlanProdXTurno(HashMap<String,Integer> planProdXTurno) {
        this.planProdXTurno = planProdXTurno;
    }
}
